# FluentEntityMappingBuilder.java

    package org.codefilarete.stalactite.dsl.entity;

    import java.util.Collection;
    import java.util.Date;
    import java.util.List;
    import java.util.Map;
    import java.util.Set;
    import java.util.function.Consumer;
    import java.util.function.Function;
    import java.util.function.Supplier;

    import org.codefilarete.reflection.SerializablePropertyAccessor;
    import org.codefilarete.reflection.SerializablePropertyMutator;
    import org.codefilarete.stalactite.dsl.ExtraTablePropertyOptions;
    import org.codefilarete.stalactite.dsl.FluentMappings;
    import org.codefilarete.stalactite.dsl.InheritanceOptions;
    import org.codefilarete.stalactite.dsl.PersisterBuilder;
    import org.codefilarete.stalactite.dsl.PolymorphismPolicy;
    import org.codefilarete.stalactite.dsl.embeddable.EmbeddableMappingConfiguration;
    import org.codefilarete.stalactite.dsl.embeddable.EmbeddableMappingConfigurationProvider;
    import org.codefilarete.stalactite.dsl.embeddable.FluentEmbeddableMappingBuilder;
    import org.codefilarete.stalactite.dsl.embeddable.ImportedEmbedWithColumnOptions;
    import org.codefilarete.stalactite.dsl.idpolicy.IdentifierPolicy;
    import org.codefilarete.stalactite.dsl.key.CompositeKeyMappingConfigurationProvider;
    import org.codefilarete.stalactite.dsl.key.FluentEntityMappingBuilderCompositeKeyOptions;
    import org.codefilarete.stalactite.dsl.key.FluentEntityMappingBuilderKeyOptions;
    import org.codefilarete.stalactite.dsl.naming.AssociationTableNamingStrategy;
    import org.codefilarete.stalactite.dsl.naming.ColumnNamingStrategy;
    import org.codefilarete.stalactite.dsl.naming.ElementCollectionTableNamingStrategy;
    import org.codefilarete.stalactite.dsl.naming.ForeignKeyNamingStrategy;
    import org.codefilarete.stalactite.dsl.naming.JoinColumnNamingStrategy;
    import org.codefilarete.stalactite.dsl.naming.MapEntryTableNamingStrategy;
    import org.codefilarete.stalactite.dsl.naming.TableNamingStrategy;
    import org.codefilarete.stalactite.dsl.naming.UniqueConstraintNamingStrategy;
    import org.codefilarete.stalactite.dsl.property.ColumnOptions;
    import org.codefilarete.stalactite.dsl.property.ElementCollectionOptions;
    import org.codefilarete.stalactite.dsl.property.EmbeddableCollectionOptions;
    import org.codefilarete.stalactite.dsl.property.EnumOptions;
    import org.codefilarete.stalactite.dsl.property.MapOptions;
    import org.codefilarete.stalactite.dsl.relation.ManyToManyOptions;
    import org.codefilarete.stalactite.dsl.relation.OneToManyOptions;
    import org.codefilarete.stalactite.engine.PersistenceContext;
    import org.codefilarete.stalactite.sql.ddl.Size;
    import org.codefilarete.stalactite.sql.ddl.structure.Column;
    import org.codefilarete.stalactite.sql.ddl.structure.Table;
    import org.codefilarete.stalactite.sql.statement.binder.ParameterBinder;
    import org.codefilarete.tool.function.Converter;
    import org.codefilarete.tool.function.Serie;

    /**
     * An interface describing a fluent way to declare the persistence mapping of a class.
     * Please note that it can't extend {@link FluentEmbeddableMappingBuilder} because it clashes on the {@link #build(PersistenceContext)} methods that don't
     * have compatible return type.
     *
     * @author Guillaume Mary
     * @see FluentMappings#entityBuilder(Class, Class)
     * @see #build(PersistenceContext)
     */
    public interface FluentEntityMappingBuilder<C, I> extends PersisterBuilder<C, I>, EntityMappingConfigurationProvider<C, I> {

    	/**
    	 * Maps given property as identifier, using given {@link IdentifierPolicy}.
    	 * By default, the entity no-arg constructor is used to instantiate them, but you may change this behavior thanks to one of the
    	 * {@link FluentEntityMappingBuilderKeyOptions#usingConstructor(Function)} methods
    	 *
    	 * @param getter getter of the property to be used as key
    	 * @param identifierPolicy {@link IdentifierPolicy} to be used for entity insertion
    	 * @return an object for configuration chaining
    	 */
    	FluentEntityMappingBuilderKeyOptions<C, I> mapKey(SerializablePropertyAccessor<C, I> getter, IdentifierPolicy<I> identifierPolicy);

    	/**
    	 * Maps given property as identifier, using given {@link IdentifierPolicy}, and given {@link Column}.
    	 * By default, the entity no-arg constructor is used to instantiate them, but you may change this behavior thanks to one of the
    	 * {@link FluentEntityMappingBuilderKeyOptions#usingConstructor(Function)} methods
    	 *
    	 * @param getter getter of the property to be used as key
    	 * @param identifierPolicy {@link IdentifierPolicy} to be used for entity insertion
    	 * @return an object for configuration chaining
    	 */
    	<T extends Table> FluentEntityMappingBuilderKeyOptions<C, I> mapKey(SerializablePropertyAccessor<C, I> getter, IdentifierPolicy<I> identifierPolicy, Column<T, I> column);

    	/**
    	 * Maps given property as identifier, using given {@link IdentifierPolicy}, and given column name.
    	 * By default, the entity no-arg constructor is used to instantiate them, but you may change this behavior thanks to one of the
    	 * {@link FluentEntityMappingBuilderKeyOptions#usingConstructor(Function)} methods
    	 *
    	 * @param getter getter of the property to be used as key
    	 * @param identifierPolicy {@link IdentifierPolicy} to be used for entity insertion
    	 * @return an object for configuration chaining
    	 */
    	FluentEntityMappingBuilderKeyOptions<C, I> mapKey(SerializablePropertyAccessor<C, I> getter, IdentifierPolicy<I> identifierPolicy, String columnName);

    	/**
    	 * Maps given property as identifier, using given {@link IdentifierPolicy}.
    	 * By default, the entity no-arg constructor is used to instantiate them.
    	 *
    	 * @param setter setter of the property to be used as key
    	 * @param identifierPolicy {@link IdentifierPolicy} to be used for entity insertion
    	 * @return an object for configuration chaining
    	 */
    	FluentEntityMappingBuilderKeyOptions<C, I> mapKey(SerializablePropertyMutator<C, I> setter, IdentifierPolicy<I> identifierPolicy);

    	/**
    	 * Maps given property as identifier, using given {@link IdentifierPolicy}, and given {@link Column}.
    	 * By default, the entity no-arg constructor is used to instantiate them.
    	 *
    	 * @param setter setter of the property to be used as key
    	 * @param identifierPolicy {@link IdentifierPolicy} to be used for entity insertion
    	 * @return an object for configuration chaining
    	 */
    	<T extends Table> FluentEntityMappingBuilderKeyOptions<C, I> mapKey(SerializablePropertyMutator<C, I> setter, IdentifierPolicy<I> identifierPolicy, Column<T, I> column);

    	/**
    	 * Maps given property as identifier, using given {@link IdentifierPolicy}, and given column name.
    	 * By default, the entity no-arg constructor is used to instantiate them.
    	 *
    	 * @param setter setter of the property to be used as key
    	 * @param identifierPolicy {@link IdentifierPolicy} to be used for entity insertion
    	 * @return an object for configuration chaining
    	 */
    	FluentEntityMappingBuilderKeyOptions<C, I> mapKey(SerializablePropertyMutator<C, I> setter, IdentifierPolicy<I> identifierPolicy, String columnName);

    	/**
    	 * Maps given property as a composite-identifier. The {@link IdentifierPolicy} is already-assigned.
    	 * By default, the entity no-arg constructor is used to instantiate them.
    	 *
    	 * @param getter getter of the property to be used as key
    	 * @param compositeKeyMappingBuilder a configuration that details the properties that composes the identifier
    	 * @return an object for configuration chaining
    	 */
    	default FluentEntityMappingBuilderCompositeKeyOptions<C, I> mapKey(SerializablePropertyAccessor<C, I> getter,
    																	   CompositeKeyMappingConfigurationProvider<I> compositeKeyMappingBuilder) {
    		return mapKey(getter, compositeKeyMappingBuilder, null, null);
    	}

    	/**
    	 * Maps given property as a composite-identifier. The {@link IdentifierPolicy} is already-assigned.
    	 * By default, the entity no-arg constructor is used to instantiate them.
    	 *
    	 * @param setter setter of the property to be used as key
    	 * @param compositeKeyMappingBuilder a configuration that details the properties that composes the identifier
    	 * @return an object for configuration chaining
    	 */
    	default FluentEntityMappingBuilderCompositeKeyOptions<C, I> mapKey(SerializablePropertyMutator<C, I> setter,
    																	   CompositeKeyMappingConfigurationProvider<I> compositeKeyMappingBuilder) {
    		return mapKey(setter, compositeKeyMappingBuilder, null, null);
    	}

    	/**
    	 * Maps given property as a composite-identifier. The {@link IdentifierPolicy} is already-assigned.
    	 * By default, the entity no-arg constructor is used to instantiate them.
    	 *
    	 * @param getter getter of the property to be used as key
    	 * @param compositeKeyMappingBuilder a configuration that details the properties that composes the identifier
    	 * @param markAsPersistedFunction the {@link Consumer} that allows to mark the entity as "inserted in database"
    	 * @param isPersistedFunction the {@link Function} that allows to know if entity was already inserted in database
    	 * @return an object for configuration chaining
    	 */
    	FluentEntityMappingBuilderCompositeKeyOptions<C, I> mapKey(SerializablePropertyAccessor<C, I> getter,
    															   CompositeKeyMappingConfigurationProvider<I> compositeKeyMappingBuilder,
    															   Consumer<C> markAsPersistedFunction,
    															   Function<C, Boolean> isPersistedFunction);

    	/**
    	 * Maps given property as a composite-identifier. The {@link IdentifierPolicy} is already-assigned.
    	 * By default, the entity no-arg constructor is used to instantiate them.
    	 *
    	 * @param setter setter of the property to be used as key
    	 * @param compositeKeyMappingBuilder a configuration that details the properties that composes the identifier
    	 * @param markAsPersistedFunction the {@link Consumer} that allows to mark the entity as "inserted in database"
    	 * @param isPersistedFunction the {@link Function} that allows to know if entity was already inserted in database
    	 * @return an object for configuration chaining
    	 */
    	FluentEntityMappingBuilderCompositeKeyOptions<C, I> mapKey(SerializablePropertyMutator<C, I> setter,
    															   CompositeKeyMappingConfigurationProvider<I> compositeKeyMappingBuilder,
    															   Consumer<C> markAsPersistedFunction,
    															   Function<C, Boolean> isPersistedFunction);

    	/**
    	 * Declares the attribute behind the given accessor reference as persistent.
    	 *
    	 * @param getter the accessor that gets the property
    	 * @return an object for configuration chaining
    	 * @param <O> the attribute type
    	 */
    	<O> FluentMappingBuilderPropertyOptions<C, I, O> map(SerializablePropertyAccessor<C, O> getter);

    	/**
    	 * Declares the attribute behind the given mutator reference as persistent.
    	 *
    	 * @param setter the mutator that sets the property
    	 * @return an object for configuration chaining
    	 * @param <O> the attribute type
    	 */
    	<O> FluentMappingBuilderPropertyOptions<C, I, O> map(SerializablePropertyMutator<C, O> setter);

    	/**
    	 * Declares the attribute behind the given field name as persistent.
    	 *
    	 * @param fieldName the field name that owns the attribute value
    	 * @return an object for configuration chaining
    	 * @param <O> the attribute type
    	 */
    	<O> FluentMappingBuilderPropertyOptions<C, I, O> map(String fieldName);

    	/**
    	 * Declares the enum-typed attribute behind given accessor reference as persistent.
    	 * Can be done with default {@link #map(SerializablePropertyAccessor)} method, but this signature ensures the type to be an enum and the returned object
    	 * provides some specific configuration dedicated to enum attribute.
    	 *
    	 * @param getter the accessor that gets the property
    	 * @return an object for configuration chaining
    	 * @param <E> the enum type
    	 */
    	<E extends Enum<E>> FluentMappingBuilderEnumOptions<C, I, E> mapEnum(SerializablePropertyAccessor<C, E> getter);

    	/**
    	 * Declares the enum-typed attribute behind given mutator reference as persistent.
    	 * Can be done with default {@link #map(SerializablePropertyMutator)} method, but this signature ensures the type to be an enum and the returned object
    	 * provides some specific configuration dedicated to enum attribute.
    	 *
    	 * @param setter the mutator that gets the property
    	 * @return an object for configuration chaining
    	 * @param <E> the enum type
    	 */
    	<E extends Enum<E>> FluentMappingBuilderEnumOptions<C, I, E> mapEnum(SerializablePropertyMutator<C, E> setter);

    	/**
    	 * Declares the enum-typed attribute behind given mutator reference as persistent.
    	 * Can be done with default {@link #map(String)} method, but this signature ensures the type to be an enum and the returned object
    	 * provides some specific configuration dedicated to enum attribute.
    	 *
    	 * @param fieldName the field name that owns the attribute value
    	 * @return an object for configuration chaining
    	 * @param <E> the enum type
    	 */
    	<E extends Enum<E>> FluentMappingBuilderEnumOptions<C, I, E> mapEnum(String fieldName);

    	<K, V, M extends Map<K, V>> FluentMappingBuilderMapOptions<C, I, K, V, M> mapMap(SerializablePropertyAccessor<C, M> getter, Class<K> keyType, Class<V> valueType);

    	<K, V, M extends Map<K, V>> FluentMappingBuilderMapOptions<C, I, K, V, M> mapMap(SerializablePropertyMutator<C, M> setter, Class<K> keyType, Class<V> valueType);

    	<O, S extends Collection<O>> FluentMappingBuilderElementCollectionOptions<C, I, O, S> mapCollection(SerializablePropertyAccessor<C, S> getter, Class<O> componentType);

    	<O, S extends Collection<O>> FluentMappingBuilderElementCollectionOptions<C, I, O, S> mapCollection(SerializablePropertyMutator<C, S> setter, Class<O> componentType);

    	<O, S extends Collection<O>> FluentMappingBuilderElementCollectionImportEmbedOptions<C, I, O, S> mapCollection(SerializablePropertyAccessor<C, S> getter, Class<O> componentType,
    																												   EmbeddableMappingConfigurationProvider<O> embeddableConfiguration);

    	<O, S extends Collection<O>> FluentMappingBuilderElementCollectionImportEmbedOptions<C, I, O, S> mapCollection(SerializablePropertyMutator<C, S> setter, Class<O> componentType,
    																												   EmbeddableMappingConfigurationProvider<O> embeddableConfiguration);

    	FluentEntityMappingBuilder<C, I> withTableNaming(TableNamingStrategy tableNamingStrategy);

    	FluentEntityMappingBuilder<C, I> withColumnNaming(ColumnNamingStrategy columnNamingStrategy);

    	FluentEntityMappingBuilder<C, I> withUniqueConstraintNaming(UniqueConstraintNamingStrategy uniqueConstraintNamingStrategy);

    	FluentEntityMappingBuilder<C, I> withJoinColumnNaming(JoinColumnNamingStrategy joinColumnNamingStrategy);

    	FluentEntityMappingBuilder<C, I> withElementCollectionTableNaming(ElementCollectionTableNamingStrategy tableNamingStrategy);

    	FluentEntityMappingBuilder<C, I> withMapEntryTableNaming(MapEntryTableNamingStrategy tableNamingStrategy);

    	FluentEntityMappingBuilder<C, I> withForeignKeyNaming(ForeignKeyNamingStrategy foreignKeyNamingStrategy);

    	/**
    	 * Sets {@link ColumnNamingStrategy} for index column of one-to-many {@link List} association
    	 * @param columnNamingStrategy maybe null, {@link ColumnNamingStrategy#INDEX_DEFAULT} will be used instead
    	 * @return this
    	 */
    	FluentEntityMappingBuilder<C, I> withIndexColumnNaming(ColumnNamingStrategy columnNamingStrategy);

    	FluentEntityMappingBuilder<C, I> withAssociationTableNaming(AssociationTableNamingStrategy associationTableNamingStrategy);

    	/**
    	 * Declares the mapping of a super class.
    	 * As a difference with {@link #mapSuperClass(EmbeddableMappingConfiguration)}, identifier policy must be defined
    	 * by given configuration (or the highest ancestor, not intermediary), not by current one : if id policy is
    	 * also-or-only defined by the current builder, an exception will be thrown at build time.
    	 * This method should be used when given configuration acts as a parent entity, maybe stored on a different table
    	 * than current one (see {@link InheritanceOptions#joiningTables()}.
    	 * Note that for now relations of given configuration are not taken into account (not implemented).
    	 *
    	 * @param mappingConfiguration a mapping configuration of a super type of the current mapped type
    	 * @return an enhanced version of {@code this} so one can add set options to the relation or add mapping to {@code this}
    	 */
    	default FluentMappingBuilderInheritanceOptions<C, I> mapSuperClass(EntityMappingConfiguration<? super C, I> mappingConfiguration) {
    		return this.mapSuperClass(() -> (EntityMappingConfiguration<C, I>) mappingConfiguration);
    	}

    	/**
    	 * Declares the mapping of a super class.
    	 * As a difference with {@link #mapSuperClass(EmbeddableMappingConfiguration)}, identifier policy must be defined
    	 * by given configuration (or the highest ancestor, not intermediary), not by current one : if id policy is
    	 * also-or-only defined by the current builder, an exception will be thrown at build time.
    	 * This method should be used when given configuration acts as a parent entity, maybe stored on a different table
    	 * than current one (see {@link InheritanceOptions#joiningTables()}.
    	 * Note that for now relations of given configuration are not taken into account (not implemented).
    	 *
    	 * @param mappingConfigurationProvider a mapping configuration of a super type of the current mapped type
    	 * @return an enhanced version of {@code this} so one can add set options to the relation or add mapping to {@code this}
    	 */
    	FluentMappingBuilderInheritanceOptions<C, I> mapSuperClass(EntityMappingConfigurationProvider<? super C, I> mappingConfigurationProvider);

    	/**
    	 * Declares the mapping of a super class.
    	 * Id policy must be defined by current configuration.
    	 * This method should be used when given configuration is reusable between entities, acting as a common and shared
    	 * configuration, with no impact on table or id policy, since table and id policy must be defined by current configuration.
    	 *
    	 * @param mappingConfiguration a mapping configuration of a super type of the current mapped type
    	 * @return an enhanced version of {@code this} to let caller pursue its configuration
    	 */
    	default FluentEntityMappingBuilder<C, I> mapSuperClass(EmbeddableMappingConfiguration<? super C> mappingConfiguration) {
    		return this.mapSuperClass(() -> (EmbeddableMappingConfiguration<C>) mappingConfiguration);
    	}

    	/**
    	 * Declares the mapping of a super class.
    	 * Id policy must be defined by current configuration.
    	 * This method should be used when given configuration is reusable between entities, acting as a common and shared
    	 * configuration, with no impact on table or id policy, since table and id policy must be defined by current configuration.
    	 *
    	 * @param mappingConfigurationProvider a mapping configuration of a super type of the current mapped type
    	 * @return an enhanced version of {@code this} to let caller pursue its configuration
    	 */
    	FluentEntityMappingBuilder<C, I> mapSuperClass(EmbeddableMappingConfigurationProvider<? super C> mappingConfigurationProvider);

    	/**
    	 * Declares a direct relation between current entity and some of type {@code O}.
    	 *
    	 * @param getter the way to get the target entity
    	 * @param mappingConfiguration the mapping configuration of the target entity
    	 * @param <O> type of target entity
    	 * @param <J> type of identifier of {@code O}
    	 * @return an enhanced version of {@code this} so one can add options to the relation or add mapping to {@code this}
    	 */
    	<O, J> FluentMappingBuilderOneToOneOptions<C, I, O> mapOneToOne(SerializablePropertyAccessor<C, O> getter,
    																	EntityMappingConfigurationProvider<? extends O, J> mappingConfiguration);

    	/**
    	 * Declares a direct relation between current entity and some of type {@code O}.
    	 *
    	 * @param setter the way to set the target entity
    	 * @param mappingConfiguration the mapping configuration of the target entity
    	 * @param <O> type of target entity
    	 * @param <J> type of identifier of {@code O}
    	 * @return an enhanced version of {@code this} so one can add options to the relation or add mapping to {@code this}
    	 */
    	<O, J> FluentMappingBuilderOneToOneOptions<C, I, O> mapOneToOne(SerializablePropertyMutator<C, O> setter,
    																	EntityMappingConfigurationProvider<? extends O, J> mappingConfiguration);

    	/**
    	 * Declares a relation between current entity and some of type {@code O} through a {@link Collection}.
    	 * Depending on collection type, order persistence can be asked by one of the {@link OneToManyOptions#indexed()}
    	 * methods.
    	 * Note that given mapping configuration has a generic signature made of {@code ? super O} to handle polymorphic case: given persister is allowed
    	 * to handle any super type of current entity type.
    	 *
    	 * @param getter the way to get the {@link Set} from source entities
    	 * @param mappingConfiguration the mapping configuration of the {@link Set} entities
    	 * @param <O> type of {@link Collection} element
    	 * @param <J> type of identifier of {@code O}
    	 * @param <S> refined {@link Collection} type
    	 * @return an enhanced version of {@code this} so one can add set options to the relation or add mapping to {@code this}
    	 * @see #mapOneToMany(SerializablePropertyMutator, EntityMappingConfigurationProvider)
    	 */
    	<O, J, S extends Collection<O>>
    	FluentMappingBuilderOneToManyOptions<C, I, O, S>
    	mapOneToMany(SerializablePropertyAccessor<C, S> getter, EntityMappingConfigurationProvider<? super O, J> mappingConfiguration);

    	/**
    	 * Declares a relation between current entity and some of type {@code O} through a {@link Collection}.
    	 * Depending on collection type, order persistence can be asked by one of the {@link OneToManyOptions#indexed()}
    	 * methods.
    	 * Note that given mapping configuration has a generic signature made of {@code ? super O} to handle polymorphic case: given persister is allowed
    	 * to handle any super type of current entity type.
    	 *
    	 * @param setter the way to set the {@link Set} from source entities
    	 * @param mappingConfiguration the mapping configuration of the {@link Set} entities
    	 * @param <O> type of {@link Collection} element
    	 * @param <J> type of identifier of {@code O}
    	 * @param <S> refined {@link Collection} type
    	 * @return an enhanced version of {@code this} so one can add set options to the relation or add mapping to {@code this}
    	 * @see #mapOneToMany(SerializablePropertyAccessor, EntityMappingConfigurationProvider)
    	 */
    	<O, J, S extends Collection<O>>
    	FluentMappingBuilderOneToManyOptions<C, I, O, S>
    	mapOneToMany(SerializablePropertyMutator<C, S> setter, EntityMappingConfigurationProvider<? super O, J> mappingConfiguration);

    	/**
    	 * Declares a many-to-many relation between current entity and some of type {@code O}.
    	 * Depending on collection type, order persistence can be asked by one of the {@link ManyToManyOptions#indexed()}
    	 * methods.
    	 * For bidirectional relation, you may be interested in using {@link ManyToManyOptions#reverseCollection(SerializablePropertyAccessor)}
    	 * or {@link ManyToManyOptions#reverselySetBy(SerializablePropertyMutator)} on returned instance.
    	 * Note that given mapping configuration has a generic signature made of {@code ? super O} to handle polymorphic case: given persister is allowed
    	 * to handle any super type of current entity type.
    	 *
    	 * @param getter the way to get the {@link Set} from source entities
    	 * @param mappingConfiguration the mapping configuration of the {@link Set} entities
    	 * @param <O> type of {@link Set} element
    	 * @param <J> type of identifier of {@code O}
    	 * @param <S1> refined source {@link Collection} type
    	 * @param <S2> refined reverse side {@link Collection} type
    	 * @return an enhanced version of {@code this} so one can add set options to the relation or add mapping to {@code this}
    	 */
    	<O, J, S1 extends Collection<O>, S2 extends Collection<C>>
    	FluentMappingBuilderManyToManyOptions<C, I, O, S1, S2>
    	mapManyToMany(SerializablePropertyAccessor<C, S1> getter, EntityMappingConfigurationProvider<? super O, J> mappingConfiguration);

    	/**
    	 * Declares a many-to-many relation between current entity and some of type {@code O}.
    	 * Depending on collection type, order persistence can be asked by one of the {@link ManyToManyOptions#indexed()}
    	 * methods.
    	 * For bidirectional relation, you may be interested in using {@link ManyToManyOptions#reverseCollection(SerializablePropertyAccessor)}
    	 * or {@link ManyToManyOptions#reverselySetBy(SerializablePropertyMutator)} on returned instance.
    	 * Note that given mapping configuration has a generic signature made of {@code ? super O} to handle polymorphic case: given persister is allowed
    	 * to handle any super type of current entity type.
    	 *
    	 * @param setter the way to get the {@link Set} from source entities
    	 * @param mappingConfiguration the mapping configuration of the {@link Set} entities
    	 * @param <O> type of {@link Set} element
    	 * @param <J> type of identifier of {@code O}
    	 * @param <S1> refined source {@link Collection} type
    	 * @param <S2> refined reverse side {@link Collection} type
    	 * @return an enhanced version of {@code this} so one can add set options to the relation or add mapping to {@code this}
    	 */
    	<O, J, S1 extends Collection<O>, S2 extends Collection<C>>
    	FluentMappingBuilderManyToManyOptions<C, I, O, S1, S2>
    	mapManyToMany(SerializablePropertyMutator<C, S1> setter, EntityMappingConfigurationProvider<? super O, J> mappingConfiguration);

    	/**
    	 * Declares a direct relation between current entity and some of type {@code O}.
    	 *
    	 * @param getter the way to get the target entity
    	 * @param mappingConfiguration the mapping configuration of the target entity
    	 * @param <O> type of target entity
    	 * @param <J> type of identifier of {@code O}
    	 * @return an enhanced version of {@code this} so one can add options to the relation or add mapping to {@code this}
    	 */
    	<O, J, S extends Collection<C>>
    	FluentMappingBuilderManyToOneOptions<C, I, O, S>
    	mapManyToOne(SerializablePropertyAccessor<C, O> getter,
    				 EntityMappingConfigurationProvider<? extends O, J> mappingConfiguration);

    	/**
    	 * Declares a many-to-one relation between current entity and some of type {@code O}.
    	 *
    	 * @param setter the way to set the target entity
    	 * @param mappingConfiguration the mapping configuration of the target entity
    	 * @param <O> type of target entity
    	 * @param <J> type of identifier of {@code O}
    	 * @return an enhanced version of {@code this} so one can add options to the relation or add mapping to {@code this}
    	 */
    	<O, J, S extends Collection<C>>
    	FluentMappingBuilderManyToOneOptions<C, I, O, S>
    	mapManyToOne(SerializablePropertyMutator<C, O> setter,
    				 EntityMappingConfigurationProvider<? extends O, J> mappingConfiguration);

    	<O> FluentMappingBuilderEmbeddableMappingConfigurationImportedEmbedOptions<C, I, O> embed(SerializablePropertyAccessor<C, O> getter, EmbeddableMappingConfigurationProvider<? extends O> embeddableMappingBuilder);

    	<O> FluentMappingBuilderEmbeddableMappingConfigurationImportedEmbedOptions<C, I, O> embed(SerializablePropertyMutator<C, O> setter, EmbeddableMappingConfigurationProvider<? extends O> embeddableMappingBuilder);

    	/**
    	 * Defines the versioning property of beans. This implies that Optimistic Locking will be applied on those beans.
    	 * Versioning policy is supported for following types:
    	 * <ul>
    	 * <li>{@link Integer} : a "+1" policy will be applied, see {@link Serie#INTEGER_SERIE}</li>
    	 * <li>{@link Long} : a "+1" policy will be applied, see {@link Serie#LONG_SERIE}</li>
    	 * <li>{@link Date} : a "now" policy will be applied, see {@link Serie#NOW_SERIE}</li>
    	 * </ul>
    	 *
    	 * @param getter the function that gives access to the versioning property
    	 * @param <V> type of the versioning property, determines versioning policy
    	 * @return this
    	 * @see #versionedBy(SerializablePropertyAccessor, Serie)
    	 */
    	<V> FluentEntityMappingBuilder<C, I> versionedBy(SerializablePropertyAccessor<C, V> getter);

    	/**
    	 * Defines the versioning property of beans. This implies that Optimistic Locking will be applied on those beans.
    	 * Versioning policy is supported for following types:
    	 * <ul>
    	 * <li>{@link Integer} : a "+1" policy will be applied, see {@link Serie#INTEGER_SERIE}</li>
    	 * <li>{@link Long} : a "+1" policy will be applied, see {@link Serie#LONG_SERIE}</li>
    	 * <li>{@link Date} : a "now" policy will be applied, see {@link Serie#NOW_SERIE}</li>
    	 * </ul>
    	 *
    	 * @param getter the function that gives access to the versioning property
    	 * @param sequence the {@link Serie} that manage version value increment
    	 * @param <V> type of the versioning property, determines versioning policy
    	 * @return this
    	 */
    	<V> FluentEntityMappingBuilder<C, I> versionedBy(SerializablePropertyAccessor<C, V> getter, Serie<V> sequence);

    	/**
    	 * Defines the versioning property of beans. This implies that Optimistic Locking will be applied on those beans.
    	 * Versioning policy is supported for following types:
    	 * <ul>
    	 * <li>{@link Integer} : a "+1" policy will be applied, see {@link Serie#INTEGER_SERIE}</li>
    	 * <li>{@link Long} : a "+1" policy will be applied, see {@link Serie#LONG_SERIE}</li>
    	 * <li>{@link Date} : a "now" policy will be applied, see {@link Serie#NOW_SERIE}</li>
    	 * </ul>
    	 *
    	 * @param setter the function that gives access to the versioning property
    	 * @param <V> type of the versioning property, determines versioning policy
    	 * @return this
    	 * @see #versionedBy(SerializablePropertyAccessor, Serie)
    	 */
    	<V> FluentEntityMappingBuilder<C, I> versionedBy(SerializablePropertyMutator<C, V> setter);

    	/**
    	 * Defines the versioning property of beans. This implies that Optimistic Locking will be applied on those beans.
    	 * Versioning policy is supported for following types:
    	 * <ul>
    	 * <li>{@link Integer} : a "+1" policy will be applied, see {@link Serie#INTEGER_SERIE}</li>
    	 * <li>{@link Long} : a "+1" policy will be applied, see {@link Serie#LONG_SERIE}</li>
    	 * <li>{@link Date} : a "now" policy will be applied, see {@link Serie#NOW_SERIE}</li>
    	 * </ul>
    	 *
    	 * @param setter the function that gives access to the versioning property
    	 * @param sequence the {@link Serie} that manage version value increment
    	 * @param <V> type of the versioning property, determines versioning policy
    	 * @return this
    	 */
    	<V> FluentEntityMappingBuilder<C, I> versionedBy(SerializablePropertyMutator<C, V> setter, Serie<V> sequence);

    	/**
    	 * Defines the versioning property of beans. This implies that Optimistic Locking will be applied on those beans.
    	 * Versioning policy is supported for following types:
    	 * <ul>
    	 * <li>{@link Integer} : a "+1" policy will be applied, see {@link Serie#INTEGER_SERIE}</li>
    	 * <li>{@link Long} : a "+1" policy will be applied, see {@link Serie#LONG_SERIE}</li>
    	 * <li>{@link Date} : a "now" policy will be applied, see {@link Serie#NOW_SERIE}</li>
    	 * </ul>
    	 *
    	 * @param fieldName the field name that gives access to the versioning property
    	 * @param <V> type of the versioning property, determines versioning policy
    	 * @return this
    	 * @see #versionedBy(SerializablePropertyAccessor, Serie)
    	 */
    	<V> FluentEntityMappingBuilder<C, I> versionedBy(String fieldName);

    	/**
    	 * Defines the versioning property of beans. This implies that Optimistic Locking will be applied on those beans.
    	 * Versioning policy is supported for following types:
    	 * <ul>
    	 * <li>{@link Integer} : a "+1" policy will be applied, see {@link Serie#INTEGER_SERIE}</li>
    	 * <li>{@link Long} : a "+1" policy will be applied, see {@link Serie#LONG_SERIE}</li>
    	 * <li>{@link Date} : a "now" policy will be applied, see {@link Serie#NOW_SERIE}</li>
    	 * </ul>
    	 *
    	 * @param fieldName the field name that gives access to the versioning property
    	 * @param sequence the {@link Serie} that manage version value increment
    	 * @param <V> type of the versioning property, determines versioning policy
    	 * @return this
    	 */
    	<V> FluentEntityMappingBuilder<C, I> versionedBy(String fieldName, Serie<V> sequence);

    	FluentEntityMappingBuilder<C, I> onTable(String tableName);

    	FluentEntityMappingBuilder<C, I> onTable(Table table);

    	FluentEntityMappingBuilder<C, I> mapPolymorphism(PolymorphismPolicy<C> polymorphismPolicy);

    	interface FluentMappingBuilderPropertyOptions<C, I, O>
    			extends
    			FluentEntityMappingBuilder<C, I>,
    			ColumnOptions<O>,
    			ExtraTablePropertyOptions {

    		@Override
    		FluentMappingBuilderPropertyOptions<C, I, O> mandatory();

    		@Override
    		FluentMappingBuilderPropertyOptions<C, I, O> nullable();

    		@Override
    		FluentMappingBuilderPropertyOptions<C, I, O> unique();

    		@Override
    		FluentMappingBuilderPropertyOptions<C, I, O> setByConstructor();

    		@Override
    		FluentMappingBuilderPropertyOptions<C, I, O> readonly();

    		@Override
    		FluentMappingBuilderPropertyOptions<C, I, O> columnName(String name);

    		@Override
    		FluentMappingBuilderPropertyOptions<C, I, O> columnSize(Size size);

    		@Override
    		FluentMappingBuilderPropertyOptions<C, I, O> column(Column<? extends Table, ? extends O> column);

    		@Override
    		FluentMappingBuilderPropertyOptions<C, I, O> fieldName(String name);

    		@Override
    		FluentMappingBuilderPropertyOptions<C, I, O> extraTable(String name);

    		@Override
    		FluentMappingBuilderPropertyOptions<C, I, O> extraTable(Table table);

    		@Override
    		<X> FluentMappingBuilderPropertyOptions<C, I, O> readConverter(Converter<X, O> converter);

    		@Override
    		<X> FluentMappingBuilderPropertyOptions<C, I, O> writeConverter(Converter<O, X> converter);

    		@Override
    		<V> FluentMappingBuilderPropertyOptions<C, I, O> sqlBinder(ParameterBinder<V> parameterBinder);
    	}

    	/**
    	 * A mashup that allows to come back to the "main" options as well as continue configuration of an "imported bean mapping"
    	 * @param <C>
    	 * @param <I>
    	 * @param <O>
    	 */
    	interface FluentMappingBuilderEmbeddableMappingConfigurationImportedEmbedOptions<C, I, O>
    			extends FluentEntityMappingBuilder<C, I>, ImportedEmbedWithColumnOptions<O> {

    		@Override
    		<IN> FluentMappingBuilderEmbeddableMappingConfigurationImportedEmbedOptions<C, I, O> overrideName(SerializablePropertyAccessor<O, IN> function, String columnName);

    		@Override
    		<IN> FluentMappingBuilderEmbeddableMappingConfigurationImportedEmbedOptions<C, I, O> overrideName(SerializablePropertyMutator<O, IN> function, String columnName);

    		@Override
    		<IN> FluentMappingBuilderEmbeddableMappingConfigurationImportedEmbedOptions<C, I, O> overrideSize(SerializablePropertyAccessor<O, IN> function, Size columnSize);

    		@Override
    		<IN> FluentMappingBuilderEmbeddableMappingConfigurationImportedEmbedOptions<C, I, O> overrideSize(SerializablePropertyMutator<O, IN> function, Size columnSize);

    		/**
    		 * Overrides embedding with an existing target column
    		 *
    		 * @param getter the getter as a method reference
    		 * @param targetColumn a column that's the target of the getter
    		 * @param <IN> input of the function (type of the embedded element)
    		 * @return a mapping configurer, specialized for embedded elements
    		 */
    		<IN> FluentMappingBuilderEmbeddableMappingConfigurationImportedEmbedOptions<C, I, O> override(SerializablePropertyAccessor<O, IN> getter, Column<? extends Table, IN> targetColumn);

    		/**
    		 * Overrides embedding with an existing target column
    		 *
    		 * @param setter the setter as a method reference
    		 * @param targetColumn a column that's the target of the getter
    		 * @param <IN> input of the function (type of the embedded element)
    		 * @return a mapping configurer, specialized for embedded elements
    		 */
    		<IN> FluentMappingBuilderEmbeddableMappingConfigurationImportedEmbedOptions<C, I, O> override(SerializablePropertyMutator<O, IN> setter, Column<? extends Table, IN> targetColumn);

    		@Override
    		<IN> FluentMappingBuilderEmbeddableMappingConfigurationImportedEmbedOptions<C, I, O> exclude(SerializablePropertyMutator<O, IN> setter);

    		@Override
    		<IN> FluentMappingBuilderEmbeddableMappingConfigurationImportedEmbedOptions<C, I, O> exclude(SerializablePropertyAccessor<O, IN> getter);
    	}

    	interface FluentMappingBuilderEnumOptions<C, I, E extends Enum<E>>
    			extends FluentEntityMappingBuilder<C, I>, EnumOptions<E> {

    		@Override
    		FluentMappingBuilderEnumOptions<C, I, E> byName();

    		@Override
    		FluentMappingBuilderEnumOptions<C, I, E> byOrdinal();

    		@Override
    		FluentMappingBuilderEnumOptions<C, I, E> mandatory();

    		@Override
    		FluentMappingBuilderEnumOptions<C, I, E> unique();

    		@Override
    		FluentMappingBuilderEnumOptions<C, I, E> readonly();

    		@Override
    		FluentMappingBuilderEnumOptions<C, I, E> columnName(String name);

    		@Override
    		FluentMappingBuilderEnumOptions<C, I, E> columnSize(Size size);

    		@Override
    		FluentMappingBuilderEnumOptions<C, I, E> column(Column<? extends Table, ? extends E> column);

    		@Override
    		FluentMappingBuilderEnumOptions<C, I, E> fieldName(String name);

    		@Override
    		<X> FluentMappingBuilderEnumOptions<C, I, E> readConverter(Converter<X, E> converter);

    		@Override
    		<X> FluentMappingBuilderEnumOptions<C, I, E> writeConverter(Converter<E, X> converter);

    		@Override
    		<V> FluentMappingBuilderEnumOptions<C, I, E> sqlBinder(ParameterBinder<V> parameterBinder);
    	}

    	interface FluentMappingBuilderInheritanceOptions<C, I>
    			extends FluentEntityMappingBuilder<C, I>, InheritanceOptions {

    		@Override
    		FluentMappingBuilderInheritanceOptions<C, I> joiningTables();
    	}

    	interface FluentMappingBuilderElementCollectionOptions<C, I, O, S extends Collection<O>>
    			extends FluentEntityMappingBuilder<C, I>, ElementCollectionOptions<C, O, S> {

    		@Override
    		FluentMappingBuilderElementCollectionOptions<C, I, O, S> initializeWith(Supplier<? extends S> collectionFactory);

    		@Override
    		FluentMappingBuilderElementCollectionOptions<C, I, O, S> elementColumnName(String columnName);

    		@Override
    		FluentMappingBuilderElementCollectionOptions<C, I, O, S> elementColumnSize(Size columnSize);

    		@Override
    		FluentMappingBuilderElementCollectionOptions<C, I, O, S> reverseJoinColumn(String name);

    		@Override
    		FluentMappingBuilderElementCollectionOptions<C, I, O, S> indexed();

    		@Override
    		FluentMappingBuilderElementCollectionOptions<C, I, O, S> indexedBy(String columnName);

    		@Override
    		FluentMappingBuilderElementCollectionOptions<C, I, O, S> onTable(Table table);

    		@Override
    		FluentMappingBuilderElementCollectionOptions<C, I, O, S> onTable(String tableName);

    	}

    	interface FluentMappingBuilderMapOptions<C, I, K, V, M extends Map<K, V>>
    			extends FluentEntityMappingBuilder<C, I>, MapOptions<K, V, M> {

    		@Override
    		FluentMappingBuilderMapOptions<C, I, K, V, M> reverseJoinColumn(String columnName);

    		@Override
    		FluentMappingBuilderMapOptions<C, I, K, V, M> keyColumn(String columnName);

    		@Override
    		FluentMappingBuilderMapOptions<C, I, K, V, M> keySize(Size columnSize);

    		@Override
    		FluentMappingBuilderMapOptions<C, I, K, V, M> valueColumn(String columnName);

    		@Override
    		FluentMappingBuilderMapOptions<C, I, K, V, M> valueSize(Size columnSize);

    		@Override
    		FluentMappingBuilderMapOptions<C, I, K, V, M> initializeWith(Supplier<? extends M> collectionFactory);

    		@Override
    		FluentMappingBuilderMapOptions<C, I, K, V, M> onTable(String tableName);

    		@Override
    		FluentMappingBuilderMapOptions<C, I, K, V, M> onTable(Table table);

    		@Override
    		FluentMappingBuilderEntityInMapOptions<C, I, K, V, M> withKeyMapping(EntityMappingConfigurationProvider<K, ?> mappingConfigurationProvider);

    		@Override
    		FluentMappingBuilderEmbeddableInMapOptions<C, I, K, V, M, K> withKeyMapping(EmbeddableMappingConfigurationProvider<K> mappingConfigurationProvider);

    		@Override
    		FluentMappingBuilderEntityInMapOptions<C, I, K, V, M> withValueMapping(EntityMappingConfigurationProvider<V, ?> mappingConfigurationProvider);

    		@Override
    		FluentMappingBuilderEmbeddableInMapOptions<C, I, K, V, M, V> withValueMapping(EmbeddableMappingConfigurationProvider<V> mappingConfigurationProvider);

    		@Override
    		FluentMappingBuilderMapOptions<C, I, K, V, M> fetchSeparately();
    	}

    	interface FluentMappingBuilderEntityInMapOptions<C, I, K, V, M extends Map<K, V>>
    			extends FluentMappingBuilderMapOptions<C, I, K, V, M>, MapOptions.EntityInMapOptions<K, V, M> {

    		@Override
    		FluentMappingBuilderEntityInMapOptions<C, I, K, V, M> cascading(RelationMode relationMode);
    	}

    	interface FluentMappingBuilderEmbeddableInMapOptions<C, I, K, V, M extends Map<K, V>, E>
    			extends FluentMappingBuilderMapOptions<C, I, K, V, M>, MapOptions.EmbeddableInMapOptions<E> {

    		@Override
    		FluentMappingBuilderEmbeddableInMapOptions<C, I, K, V, M, E> overrideName(SerializablePropertyAccessor<E, ?> getter, String columnName);

    		@Override
    		FluentMappingBuilderEmbeddableInMapOptions<C, I, K, V, M, E> overrideName(SerializablePropertyMutator<E, ?> setter, String columnName);

    		@Override
    		FluentMappingBuilderEmbeddableInMapOptions<C, I, K, V, M, E> overrideSize(SerializablePropertyAccessor<E, ?> getter, Size columnSize);

    		@Override
    		FluentMappingBuilderEmbeddableInMapOptions<C, I, K, V, M, E> overrideSize(SerializablePropertyMutator<E, ?> setter, Size columnSize);
    	}

    	interface FluentMappingBuilderElementCollectionImportEmbedOptions<C, I, O, S extends Collection<O>>
    			extends FluentEntityMappingBuilder<C, I>, EmbeddableCollectionOptions<C, O, S> {

    		@Override
    		<IN> FluentMappingBuilderElementCollectionImportEmbedOptions<C, I, O, S> overrideName(SerializablePropertyAccessor<O, IN> getter, String columnName);

    		@Override
    		<IN> FluentMappingBuilderElementCollectionImportEmbedOptions<C, I, O, S> overrideName(SerializablePropertyMutator<O, IN> setter, String columnName);

    		@Override
    		<IN> FluentMappingBuilderElementCollectionImportEmbedOptions<C, I, O, S> overrideSize(SerializablePropertyAccessor<O, IN> getter, Size columnSize);

    		@Override
    		<IN> FluentMappingBuilderElementCollectionImportEmbedOptions<C, I, O, S> overrideSize(SerializablePropertyMutator<O, IN> setter, Size columnSize);

    		@Override
    		FluentMappingBuilderElementCollectionImportEmbedOptions<C, I, O, S> initializeWith(Supplier<? extends S> collectionFactory);

    		@Override
    		FluentMappingBuilderElementCollectionImportEmbedOptions<C, I, O, S> reverseJoinColumn(String name);

    		@Override
    		FluentMappingBuilderElementCollectionImportEmbedOptions<C, I, O, S> indexed();

    		@Override
    		FluentMappingBuilderElementCollectionImportEmbedOptions<C, I, O, S> indexedBy(String columnName);

    		@Override
    		FluentMappingBuilderElementCollectionImportEmbedOptions<C, I, O, S> onTable(Table table);

    		@Override
    		FluentMappingBuilderElementCollectionImportEmbedOptions<C, I, O, S> onTable(String tableName);
    	}
    }